﻿#region License
/* Copyright (C) 2009 Tim Coulter
 * 
 * This file is part of ExtremeML.
 * 
 * ExtremeML is free software: you can redistribute
 * it and/or modify it under the terms of the GNU General Public
 * License, as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 * 
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty
 * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
 * the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see http://www.gnu.org/licenses/.
*/
#endregion

using System;
using System.Collections.Generic;
using System.IO;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using ExtremeML.Core;

namespace ExtremeML.Packaging
{
	/// <summary>
	/// Wraps a <see cref="SpreadsheetDocument"/> and exposes its content as an object graph.
	/// </summary>
	public partial class SpreadsheetDocumentWrapper : PackageWrapper<SpreadsheetDocument>
	{
		#region Housekeeping

		/// <summary>
		/// Initializes a new instance of the <see cref="SpreadsheetDocumentWrapper"/> class.
		/// </summary>
		/// <param name="package">A <see cref="SpreadsheetDocument"/> from which to populate this instance.</param>
		/// <param name="source">The <see cref="Stream"/> from which the wrapped package was populated.</param>
		/// <param name="target">The <see cref="Stream"/> to which the result will be written.</param>
		protected SpreadsheetDocumentWrapper(
			SpreadsheetDocument package,
			Stream source,
			Stream target)
			: base(package, source, target)
		{
			Activate();
		}

		#endregion
		#region Custom Properties

		/// <summary>
		/// Gets the contained <see cref="WorkbookPartWrapper"/>.
		/// </summary>
		public WorkbookPartWrapper WorkbookPart { get; private set; }

		#endregion
		#region Custom Methods


		#endregion
		#region Overrides

		/// <summary>
		/// Decomposes the wrapped package into its parts.
		/// </summary>
		protected override void OnDecomposing()
		{
			base.OnDecomposing();
			WorkbookPart = new WorkbookPartWrapper(Wrapped.WorkbookPart, this, this);
		}

		/// <summary>
		/// Re-creates the wrapped package from its parts.
		/// </summary>
		protected override void Recompose()
		{
			if (WorkbookPart != null)
			{
				WorkbookPart.Recompose(Wrapped);
			}
		}

		/// <summary>
		/// Returns the set of <see cref="IContentWrapper"/> that are exposed as properties.
		/// </summary>
		protected override IEnumerable<IWrapper> GetWrapperMembers()
		{
			yield return WorkbookPart;
		}

		#endregion
		#region Implementation Methods


		#endregion
		#region Static Methods

		/// <summary>
		/// Creates a new empty <see cref="SpreadsheetDocumentWrapper"/> at the specified package path.
		/// </summary>
		/// <param name="path">A <see cref="string"/> that specifies the path to an OpenXml package file.</param>
		/// <returns>A new instance of <see cref="SpreadsheetDocumentWrapper"/>.</returns>
		public static SpreadsheetDocumentWrapper Create(
			string path)
		{
			return new SpreadsheetDocumentWrapper(CreateDefaultPackage(path), null, null);
		}

		/// <summary>
		/// Creates a <see cref="SpreadsheetDocumentWrapper"/> and populates it from the package at the specified path.
		/// </summary>
		/// <param name="path">A <see cref="string"/> that specifies the path to an OpenXml package file.</param>
		/// <returns>A new instance of <see cref="SpreadsheetDocumentWrapper"/>.</returns>
		public static SpreadsheetDocumentWrapper Open(
			string path)
		{
			var source = new FileStream(path, FileMode.Open);
			return new SpreadsheetDocumentWrapper(SpreadsheetDocument.Open(source, true), source, null);
		}

		/// <summary>
		/// Creates a new empty <see cref="SpreadsheetDocumentWrapper"/> at the specified package path.
		/// </summary>
		/// <param name="target">A <see cref="Stream"/> to which to write the modified OpenXml package.</param>
		/// <returns>A new instance of <see cref="SpreadsheetDocumentWrapper"/>.</returns>
		public static SpreadsheetDocumentWrapper Create(
			Stream target)
		{
			return new SpreadsheetDocumentWrapper(CreateDefaultPackage(target), null, null);
		}

		/// <summary>
		/// Creates a <see cref="SpreadsheetDocumentWrapper"/> and populates it from the passed stream.
		/// </summary>
		/// <param name="source">A <see cref="Stream"/> from which to load the OpenXml template package.</param>
		/// <param name="target">A <see cref="Stream"/> to which to write the modified OpenXml package.</param>
		/// <returns>A new instance of <see cref="SpreadsheetDocumentWrapper"/>.</returns>
		public static SpreadsheetDocumentWrapper Open(
			Stream source,
			Stream target)
		{
			if (source == null) throw new ArgumentNullException("source");

			if (Equals(source, target) || target == null)
			{
				// When targeting the source stream there is no need for an intermediate MemoryStream
				return new SpreadsheetDocumentWrapper(SpreadsheetDocument.Open(source, true), null, null);
			}
			else
			{
				// An intermediate content stream allows the output to be intercepted and redirected
				var buffer = new byte[(int)source.Length];
				var content = new MemoryStream(buffer.Length);

				source.Position = 0;
				source.Read(buffer, 0, buffer.Length);

				// In keeping with the behavior of the SDK, the source stream is not disposed,
				// as it may be subsequently required by the client application.

				content.Write(buffer, 0, buffer.Length);

				return new SpreadsheetDocumentWrapper(SpreadsheetDocument.Open(content, true), content, target);
			}
		}

		/// <summary>
		/// Returns a <see cref="SpreadsheetDocument"/> populated with default content.
		/// </summary>
		/// <param name="target">A <see cref="Stream"/> to which to write the modified OpenXml package.</param>
		/// <returns>A new instance of <see cref="SpreadsheetDocument"/>.</returns>
		private static SpreadsheetDocument CreateDefaultPackage(
			Stream target)
		{
			var package = SpreadsheetDocument.Create(target, SpreadsheetDocumentType.Workbook);

			WorkbookPartWrapper.CreateDefaultPart(package);

			return package;
		}

		/// <summary>
		/// Returns a <see cref="SpreadsheetDocument"/> populated with default content.
		/// </summary>
		/// <param name="path">The path and file name of the target Package.</param>
		/// <returns>A new instance of <see cref="SpreadsheetDocument"/>.</returns>
		private static SpreadsheetDocument CreateDefaultPackage(
			string path)
		{
			var package = SpreadsheetDocument.Create(path, SpreadsheetDocumentType.Workbook);

			WorkbookPartWrapper.CreateDefaultPart(package);

			return package;
		}

		/// <summary>
		/// Returns a globally unique identifier for assignment to new document content objects.
		/// </summary>
		/// <returns>A unique Id string.</returns>
		internal static string GetUniqueIdString()
		{
			return Guid.NewGuid().ToString().Replace("-", "");
		}

		#endregion
	}
}
